package com.elione.modbus;

/**
 * A class provide a set of function for data type coversion
 *
 * @author     Zhang hongyu <hongyu.zhang@natcon7.com>
 *
 * @link      
 *
 * @see  
 *
 * @version    1.0 (22.08.2004)
 * @version    2.0 (13.04.2005)
 *  - new function to convert MODBUS raw Value 
 */

public class DataTypeUtil {

    
  
  /** 
   * Convert bytes input to an integer value. Byte3 = MSB, Byte0 = LSB. <br>
   * The maximum positive return integer is 2147483647 (0x7f ff ff ff)
   *    
   * @param   b3      MSB 
   * @param   b2      byte 2 
   * @param   b1      byte 1
   * @param   b0      LSB       
   *
   * @return  interger value which MSB is bv[3],LSB is  bv[0]; 
   *
   * @since   1.0
   */    
  public static int bytes2Int(byte b3,byte b2,byte b1, byte b0) {
    int i = 0;
    i += unsignedByteToInt(b3) << 24;
    i += unsignedByteToInt(b2) << 16;
    i += unsignedByteToInt(b1) << 8;
    i += unsignedByteToInt(b0) << 0;   
    
    return (i);
  }

  
  /** 
   * Convert a byte input to an <B> unsigned Integer value </B>
   * e.g. unsignedByteToInt(127) = 127; <br>
   * unsignedByteToInt(-128) = 128; unsignedByteToInt(-1) = 255
   *    
   * @param   b     input byte 
   *
   * @return  Unsigned interger value 
   *
   * @since   1.0
   */     
  public static int unsignedByteToInt(byte b) {
    return (int) b & 0xFF;
  }


  /**
   * Convert received raw byte value to the following java object <br> 
   *   Type code                                       Return Object type <br>
   *   2  - Integer value ( 16 bit, MSB is sign bit )  Short  <br>
   *   3  � long value (32 bit, MSB is sign bit )      Integer <br> 
   *   4  � float value  ( 32 bit IEEE754  )           Float <br> 
   *   11 � boolean value ( 1 bit )                    Boolean  <br> 
   *   17 � byte value ( 8 bit )                       Integer  <br>
   *   18 � unsigned integer value ( 16 bit )          Integer  <br>
   *   19 � unsigned long value ( 32 bit )             Long <br> 
   *
   * @since 1.0 
   *                                           
   * @param b3        raw value byte 3, default MSB
   * @param b4        raw value byte 2 
   * @param b1        raw value byte 1 
   * @param b0        raw value byte 0  default LSB
   * @param swap      If swap input byte.  <br>
   *                  swap = false -> value is (b3,b2,b1,b0) where b3 is MSB; b0 is LSB <br>
   *                  swap = true  -> value is (b1,b0,b3,b2) where b1 is MSB; b2 is LSB <br>
   * @param vt        NC2 data type 
   * 
   * @return          Interger(0) if no suitable type founded   
   *                     
   */    
  public static Object covertRawValue( 
    byte b3, byte b2, byte b1, byte b0, boolean swap, int vt) {
    
//    System.out.println ("input is " + b3 + " " + b2 + " " + b1 + " " + b0 + " " + swap);
   
    int raw_int = 0;    
    // check if need to swap value 
    if (swap) {
      raw_int = DataTypeUtil.bytes2Int(b1,b0,b3,b2);          
    } else  {
      raw_int = DataTypeUtil.bytes2Int(b3,b2,b1,b0);          
    }

    Object value = null;
    
    switch (vt) {
     case 2:  // java short value (16 bit , MSB is sign bit)
       value = new Short((short)raw_int);
     break;
     case 3:  // java integer value (32 bit , MSB is sign bit)
       value = new Integer(raw_int);
     break;     
     case 4:  // java float value (32 bit IEEE754 format )
       // convert from IEEE 754 int string to float value
       value = new Float(Float.intBitsToFloat(raw_int)); 
     break;          
     case 11:  // java boolean value (1 bit)
        value = new Boolean(raw_int != 0);
     break;          
     case 17:  // java integer value ( 8 bit unsign )
       value = new Integer(raw_int);                   
     break;                    
     case 18:  // java integer value ( 16 bit unsign ) 
       value = new Integer (raw_int);
     break;             
     case 19:  // java long value (32 bit unsign)         
       long longValue = (long) raw_int & 0x00000000FFFFFFFFL;       
       value = new Long(longValue);
     break;
     default:
       value = new Integer(0);
    }
    return (value);
  }
  
  /**
   * Convert raw byte array to java object 
   *
   * @see covertRawValue
   *
   * @since 1.0 
   *
   * @param raw       raw value of array value 
   * @param nc2type   array NC2 opc data type 
   * @param swap      If swap input bytes in calculation  
   *                  swap = false -> item value is (b3,b2,b1,b0) which b3 is MSB; b0 is LSB
   *                  swap = true  -> item value is (b1,b0,b3,b2) which b1 is MSB; b2 is LSB
   * @param vt        NC2 data type, e.g. VT_UI4,VT_UI1 
   * 
   * @return          int[] array with 0 of each item  if no suitable type founded 
   *                    
   * @throws          FunctionIllegalInputException if funciton has illegal input 
   */    
  public static Object convertArrayRawValue(byte[] raw,int nc2type, boolean swap) 
    throws FunctionIllegalInputException {
    // check input parameter 
    if (raw.length<2) {
      throw ( new FunctionIllegalInputException(
      "convertArrayRawValue has illegal input,raw value length is [" + raw.length +"]") );
    }
    
    int len = swap?4:2;       // define signal length, WORD = 2, DWORD = 4
    
    if ( (raw.length % len) != 0) {
      throw ( new FunctionIllegalInputException(
      "convertArrayRawValue has illegal input,raw value lenght is [" + raw.length +"]") );    
    }
    
    // calculate array size
    int size = raw.length / len; 
    
    // prepare array     
    boolean[] boolArray = null;
    byte[] byteArray = null;    
    int[] intArray = null;
    float[] floatArray = null;
    long[] longArray = null;
    short[] shortArray = null;
    
    
    // Set a flag to indicate which array, 
    // 1 - bool array, 2 - byte array, 3 - int array, 4 - float array , 5 - long array ,6 - short array 
    int arrayNumber = 3; 
    
    // create result array by nc2Type
    switch (nc2type) {    
      case 2:  // java short value (16 bit , MSB is sign bit)
        shortArray    = new short[size];
        arrayNumber = 6;
      break;
      case 3:  // java integer value (32 bit , MSB is sign bit)
        intArray    = new int[size];
        arrayNumber = 3;
      break;     
      case 4:  // java float value (32 bit IEEE754 format ) 
        floatArray  = new float[size]; 
        arrayNumber = 4;
      break;          
      case 11:  // java boolean value (1 bit)
        boolArray = new boolean[size];
        arrayNumber = 1;
      break;                       
      case 17:  // java integer value ( 8 bit unsign )             
        intArray = new int[size];
        arrayNumber = 3;
      break;                    
      case 18:  // java integer value ( 16 bit unsign )
        intArray    = new int[size];
        arrayNumber = 3; 
      break;             
      case 19:  // java long value (32 bit unsign)
        longArray = new long[size];
        arrayNumber = 5; 
      break;                       
                
      default:
        intArray    = new int[size];
        arrayNumber = 3;
    }    
    
    
            
    Object result; 
    int j = 0;
    for (int i=0;i<raw.length;i+=len,j++) {
      byte b3 = 0;
      byte b2 = 0;
      byte b1 = 0;
      byte b0 = 0;    
      
      if (swap) {    
        // calculation of DWORD
        b3 = raw[i + 0];     // MSB of DWORD signal 
        b2 = raw[i + 1];            
        b1 = raw[i + 2];     // 
        b0 = raw[i + 3];     // LSB for DWORD signal         
      } else  {
        // calculation of WORD
        b1 = raw[i + 0];     // MSB of WORD siganl
        b0 = raw[i + 1];     // LSB of WORD signal        
      }      
      
      // calculate each item value 
      switch (arrayNumber) {      
       case 1:  // boolArray 
         boolArray[j] = ((Boolean)covertRawValue(b3,b2,b1,b0,swap,nc2type)).booleanValue();
       break;
       case 2:  // byte array
         // modified by ZHA , 14.01.2005 
         // use special code to handle byte array value
       
         int raw_int = 0;    
         // check if need to swap value 
         if (swap) {
           raw_int = DataTypeUtil.bytes2Int(b1,b0,b3,b2);          
         } else  {
           raw_int = DataTypeUtil.bytes2Int(b3,b2,b1,b0);          
         }
     
         byteArray[j] = (new Integer(raw_int)).byteValue();
         
         
       break;
       case 3:  // int array
         intArray[j] =  ((Integer)covertRawValue(b3,b2,b1,b0,swap,nc2type)).intValue();
       break; 
       case 4:  // float array 
         floatArray[j] = ((Float)covertRawValue(b3,b2,b1,b0,swap,nc2type)).floatValue();
       break;
       case 5:  // long array 
         longArray[j] = ((Long)covertRawValue(b3,b2,b1,b0,swap,nc2type)).longValue();
       break;      
       case 6:  // short array 
         shortArray[j] = ((Short)covertRawValue(b3,b2,b1,b0,swap,nc2type)).shortValue();
       break;         
       default:                    
         intArray[j] =  ((Integer)covertRawValue(b3,b2,b1,b0,swap,nc2type)).intValue();
      }
      
    }// end for 
    
    // calculate return which array 
    switch (arrayNumber) {
     case 1:
       result = boolArray;
     break;
     case 2:
       result = byteArray;
     break;
     case 3:
       result = intArray;
     break;
     case 4:
       result = floatArray;
     break;
     case 5:
       result = longArray;
     break;
     case 6:
       result = shortArray;
     break;     
     default:
       result = intArray;     
    }
 
    return(result);
  
  }    
  
    
  
}